//
//  StatusListView.m
//  TwitterPlane
//
//  Created by kazuyuki takahashi on 10/01/23.
//  Copyright 2010 by invisibledesigner.com. All rights reserved.
//

#import "StatusListView.h"
#import "StatusViewController.h"
#import "Status.h"

#import "NSUserDefaults+App.h"
#import "Notifications.h"
#import "Utils.h"


const CGFloat kStatusListViewMargin = 2.0;

//KVO context
static NSString* const kObservationContextDisplayNameOption = @"DisplayNameOptionContext";
static NSString* const kObservationContextDisplayDateOption = @"DisplayDateOptionContext";

//新着ステータスが画面内にせり出して表示される時間
static const NSTimeInterval kNewStatusPopinTimeInterval = 10;

//マウスが離れてからviewが隠れるまでの時間
static const NSTimeInterval kAutoHideTimeIntervalMouseExited = 3;

//ステータスが隠れているときに画面内にはみ出ている幅
static const CGFloat kVisibleWidthAtHidden = 78;


//view内にマウスポインタがあるか調べる、ただしStatusListView自体は透明ビューなので除外
static BOOL mouseInView(StatusListView *view)
{
    NSCParameterAssert(view != nil);
    NSPoint pscreen = [NSEvent mouseLocation];
    NSPoint pbase = [[view window] convertScreenToBase:pscreen];
    NSPoint pself = [view convertPointFromBase:pbase];
    NSView *hitview = [view hitTest:pself];
    return (hitview != nil && hitview != view);
}


//private methods
@interface StatusListView()
- (void)applicationDidBecomeOrResignActive:(NSNotification *)notification;
- (void)refreshViews;
- (void)timerFired:(NSTimer*)timer;
- (void)doLayout;
- (void)refreshStatusViewControllers;
- (void)mouseExitedFromWindow:(NSNotification *)notification;
@end


@implementation StatusListView

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        statusViewControllers_ = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)awakeFromNib
{
    NSUserDefaultsController *udcon = [NSUserDefaultsController sharedUserDefaultsController];
    [udcon addObserver:self
            forKeyPath:[@"values." stringByAppendingString:kUserDefaultsKeyDisplayNameOption]
               options:NSKeyValueObservingOptionNew
               context:kObservationContextDisplayNameOption];
    [udcon addObserver:self
            forKeyPath:[@"values." stringByAppendingString:kUserDefaultsKeyDisplayDateOption]
               options:NSKeyValueObservingOptionNew
               context:kObservationContextDisplayDateOption];
    
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self 
           selector:@selector(applicationDidBecomeOrResignActive:) 
               name:NSApplicationDidBecomeActiveNotification 
             object:NSApp];
    [nc addObserver:self
           selector:@selector(applicationDidBecomeOrResignActive:)
               name:NSApplicationDidResignActiveNotification
             object:NSApp];
    [nc addObserver:self
           selector:@selector(mouseExitedFromWindow:)
               name:TwitterWindowMouseExitedNotification
             object:nil];
    [nc addObserver:self
           selector:@selector(refreshStatusViewControllers)
               name:TwitterEngineManagerDidReceiveStatusNotification
             object:nil];
    
    dateTimer_ = [NSTimer scheduledTimerWithTimeInterval:60 
                                                  target:self 
                                                selector:@selector(timerFired:) 
                                                userInfo:nil 
                                                 repeats:YES];
    [dateTimer_ retain];
}

- (void)applicationDidBecomeOrResignActive:(NSNotification*)notification
{
    [self doLayout];
}

- (void)refreshViews
{
    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
    [queue setMaxConcurrentOperationCount:16];
    for (StatusViewController *vc in statusViewControllers_) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^(void){
            [vc prepareView];
        }];
        [queue addOperation:op];
    }
    [queue waitUntilAllOperationsAreFinished];
    [statusViewControllers_ makeObjectsPerformSelector:@selector(refreshView)];
}

- (void)timerFired:(NSTimer *)timer
{
    [self refreshViews];
    [self doLayout];
}

- (void)refreshStatusViewControllers
{
    DevLog(@"refresh");
    NSArray *arranged = [statusesController_ arrangedObjects];
    NSMutableArray *newarray = [NSMutableArray arrayWithCapacity:[arranged count]];
    
    for (Status *st in arranged) {
        BOOL isnew = YES;
        for (StatusViewController *vc in statusViewControllers_) {
            if ([[st statusID] isEqual:[[vc representedObject] statusID]]) {
                isnew = NO;
                [newarray addObject:vc];
                break;
            }
        }
        
        if (isnew) {
            StatusViewController *vc = [[[StatusViewController alloc] initWithStatus:st] autorelease];
            [newarray addObject:vc];
        }
    }
    
    [statusViewControllers_ release];
    statusViewControllers_ = [newarray retain];
    
    if (!mouseInView(self)) {
        //一番上にスクロール
        [self scrollRectToVisible:NSZeroRect];
    }
    
    [self doLayout];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    //ユーザ名表示方法設定が変更されたら
    if (context == kObservationContextDisplayNameOption || context == kObservationContextDisplayDateOption) {
        [self refreshViews];
        [self doLayout];
    }
}


- (BOOL)isFlipped
{
    return YES;
}

- (void)doLayout
{
    DevLog(@"layout");
    for (NSView *v in [[[self subviews] copy] autorelease]) {
        BOOL remove = YES;
        for (StatusViewController *vc in statusViewControllers_) {
            if ([vc view] == v) {
                remove = NO;
                break;
            }
        }
        if (remove) {
            [v removeFromSuperview];
        }
    }
    
    BOOL hasnew = NO;
    BOOL active = ([NSApp isActive] || mouseInView(self));
    popin_ = active;

    CGFloat y = 0;
    
    for (StatusViewController *vc in statusViewControllers_) {
        NSView *v = [vc view];
        NSRect frame = [v frame];
        if (![[self subviews] containsObject:v]) {
            [self addSubview:v];
            [vc viewWasAddedToSuperView];
            [v setFrameOrigin:NSMakePoint(frame.size.width, y)];
        }
        
        CGFloat x = (active) ? 0 : frame.size.width - kVisibleWidthAtHidden;
        BOOL isnew = [[[vc representedObject] receivedDate] timeIntervalSinceNow] > -kNewStatusPopinTimeInterval;
        if (isnew) {
            x = 0;
            hasnew = YES;
        }
        [[v animator] setFrameOrigin:NSMakePoint(x, y)];
        
        y += [v frame].size.height;
    }
    NSSize size = [self frame].size;
    size.height = y;
    [self setFrameSize:size];
    
    if (hasnew) {
        [autoHideTimer_ invalidate];
        [autoHideTimer_ release];
        autoHideTimer_ = [[NSTimer scheduledTimerWithTimeInterval:kNewStatusPopinTimeInterval
                                                           target:self
                                                         selector:@selector(doLayout)
                                                         userInfo:nil
                                                          repeats:NO] retain];
    }
    
    [self setNeedsDisplay:YES];
}

- (void)mouseEntered:(NSEvent *)theEvent
{
    [[NSNotificationCenter defaultCenter] postNotificationName:TwitterViewMouseEnteredNotification object:self];
    
    if (popin_) {
        return;
    }
    
    [autoHideTimer_ invalidate];
    [autoHideTimer_ release];
    autoHideTimer_ = nil;
    [self doLayout];
}

- (void)mouseExited:(NSEvent *)theEvent
{
    //do nothing
}

- (void)mouseExitedFromWindow:(NSNotification*)notification
{
    if (autoHideTimer_ != nil)
        return;
    autoHideTimer_ = [[NSTimer scheduledTimerWithTimeInterval:kAutoHideTimeIntervalMouseExited
                                                       target:self
                                                     selector:@selector(doLayout)
                                                     userInfo:nil
                                                      repeats:NO] retain];
}

- (void)dealloc
{
    NSUserDefaultsController *udcon = [NSUserDefaultsController sharedUserDefaultsController];
    [udcon removeObserver:self forKeyPath:[@"values." stringByAppendingString:kUserDefaultsKeyDisplayNameOption]];
    [udcon removeObserver:self forKeyPath:[@"values." stringByAppendingString:kUserDefaultsKeyDisplayDateOption]];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [dateTimer_ invalidate];
    [dateTimer_ release];
    
    [statusViewControllers_ release];
    [autoHideTimer_ release];
    [super dealloc];
}

@end
